﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace GerbilLib
{
    /// <summary>
    /// List of possible MPAA movie ratings
    /// </summary>
    public enum ContentRating { Unknown, G, PG, PG13, R, NC17, Unrated, TV_G, TV_PG, TV_Y, TV_Y7, TV14, TV_MA }

    /// <summary>
    /// Provides a wrapper class for the collection of MPAA movie ratings
    /// published by the IMDb in offline text file format.
    /// </summary>
    public class MpaaRating : IMediaItem
    {
        /// <summary>
        /// Regular expression for a line in the mpaa ratings file.
        /// </summary>
        private Regex regexMpaaRating = new Regex(@"MV: (?<MovieTitle>.*)\nRE: (?<RatingReason>Rated (?<Rating>\S+).*)");
        /// <summary>
        /// Default file name of ratings file
        /// </summary>
        private static readonly string MpaaRatingsFilename = "mpaa-ratings-reasons.list";
        /// <summary>
        /// Occurs when progress is made when loading or processing ratings.
        /// </summary>
        public static event EventHandler<ProgressEventArgs> ProgressEvent;

        #region .   IMediaItem Interface Properties   .
        /// <summary>
        /// Gets or sets the title.
        /// </summary>
        /// <value>
        /// The title.
        /// </value>
        public string Title { get; set; }
        /// <summary>
        /// Gets or sets the production year.
        /// </summary>
        /// <value>
        /// The production year.
        /// </value>
        public int Year { get; set; }
        /// <summary>
        /// Gets or sets the sort weight.
        /// </summary>
        /// <value>
        /// The sort weight.
        /// </value>
        public int SortWeight { get; set; }
        #endregion
        
        /// <summary>
        /// Gets or sets the official MPAA movie rating.
        /// </summary>
        /// <value>
        /// The MPAA movie rating.
        /// </value>
        public ContentRating Rating { get; set; }
        /// <summary>
        /// Gets or sets the reason for the MPAA certification.
        /// </summary>
        /// <value>
        /// The MPAA rating reason.
        /// </value>
        public string RatingReason { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="MpaaRating"/> class.
        /// </summary>
        /// <param name="input">The input string containing the rating information.</param>
        public MpaaRating(string input)
        {
            GroupCollection gc = regexMpaaRating.Match(input).Groups;
            Title = gc["MovieTitle"].Value;
            RatingReason = gc["RatingReason"].Value;
            Rating = GetMpaaRating(gc["Rating"].Value);

            // Get year for advanced title match sorting
            GroupCollection gc2 = MediaItem.RegexLastYear.Match(input).Groups;
            int year = 0;
            int.TryParse(gc2[0].Value, out year);
            Year = year;
        }

        /// <summary>
        /// Gets the MPAA rating value from the input string.
        /// </summary>
        /// <param name="p">The input string containing an MPAA rating.</param>
        /// <returns></returns>
        private ContentRating GetMpaaRating(string p)
        {
            switch (p)
            {
                case "G": return ContentRating.G;
                case "PG": return ContentRating.PG;
                case "PG-13": return ContentRating.PG13;
                case "R": return ContentRating.R;
                case "NC-17": return ContentRating.NC17;
                default: return ContentRating.Unknown;
            }
        }

        /// <summary>
        /// Prints out the title string of the MediaItem.
        /// </summary>
        /// <returns>Title string of the MediaItem</returns>
        public override string ToString()
        {
            return Title;
        }

        /// <summary>
        /// Checks whether the ratings file already exists or needs to be downloaded first.
        /// </summary>
        /// <returns>True if the file exists, otherwise false.</returns>
        public static bool FileExists()
        {
            return File.Exists(MpaaRatingsFilename);
        }

        /// <summary>
        /// Loads the MPAA ratings from the downloaded file.
        /// </summary>
        /// <returns>HashSet of media items containing the ratings information.</returns>
        public static Dictionary<string, HashSet<IMediaItem>> LoadMpaaRatings()
        {
            Logger.LogMessage("Loading movie titles and their MPAA ratings information...");
            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            Dictionary<string, HashSet<IMediaItem>> orderedRatings = new Dictionary<string, HashSet<IMediaItem>>();
            int percentage = 0;

            stopwatch.Start();
            UpdateProgress(null, 0);
            System.IO.StreamReader sr = new System.IO.StreamReader(MpaaRatingsFilename);
            string[] items = sr.ReadToEnd().Split(new string[] { "-------------------------------------------------------------------------------" }, StringSplitOptions.RemoveEmptyEntries);
            int count = items.Count(), newPercentage = 0;

            for (int index = 0; index < count; index++)
            {
                MpaaRating m = new MpaaRating(items[index]);
                if (!string.IsNullOrEmpty(m.Title))
                {
                    //ratings.Add(m);

                    string cleanTitle = MediaItem.GetCleanTitle(m.Title);
                    HashSet<IMediaItem> ratings = null;
                    if (!orderedRatings.ContainsKey(cleanTitle))
                    {
                        ratings = new HashSet<IMediaItem>();
                        ratings.Add(m);
                        orderedRatings.Add(cleanTitle, ratings);
                    }
                    else
                    {
                        ratings = orderedRatings[cleanTitle];
                        ratings.Add(m);
                    }
                }

                newPercentage = (int)(index * 100 / items.Count());
                if (newPercentage != percentage)
                {
                    percentage = newPercentage;
                    UpdateProgress(m, percentage);
                }
            }

            UpdateProgress(null, 100);
            stopwatch.Stop();
            Logger.LogMessage(string.Format("Loaded {0} movie titles and their MPAA ratings information in {1:F} seconds!", orderedRatings.Count, stopwatch.ElapsedMilliseconds / 1000.0));
            return orderedRatings;
        }

        /// <summary>
        /// Fires the progress update event to notify any listener about changes in progress.
        /// </summary>
        /// <param name="rating">The MpaaRating object being processed.</param>
        /// <param name="percentage">The progress percentage.</param>
        public static void UpdateProgress(MpaaRating rating, int percentage)
        {
            if (ProgressEvent != null) ProgressEvent(rating, new ProgressEventArgs(percentage));
        }
    }

    /// <summary>
    /// Event arguments class for progress update events.
    /// </summary>
    public class ProgressEventArgs : EventArgs
    {
        /// <summary>
        /// Gets or sets the progress percentage.
        /// </summary>
        /// <value>
        /// The progress percentage.
        /// </value>
        public int Percentage { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProgressEventArgs"/> class.
        /// </summary>
        /// <param name="percentage">The progress percentage.</param>
        public ProgressEventArgs(int percentage)
        {
            Percentage = percentage;
        }
    }
}
